/**
 *    Copyright (C) 2009 Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_API_REFLECTION_NUMBER_HPP
#define _CUBEFS_CORE_API_REFLECTION_NUMBER_HPP

#include <cubefs/core/api/reflection/value.hpp>
#include <cubefs/api/reflection/number.hpp>

#define CUBEFS_SOURCE "cubefs/core/api/reflection/number.hpp:" CUBEFS_AT

namespace cubefs { namespace api {


template <typename Interface>
class NumberImplT: public ValueImplT<Interface> {
	typedef NumberImplT<Interface> 	Me;
	typedef ValueImplT<Interface>	Base;

public:

    static void InitType()
    {
        type_ = new AbstractType(TypeNameFactory<Interface>::name(), Value::MyType());
    }

    static void InitOperations()
    {
    	AbstractType* type = static_cast<AbstractType*>(type_);
    	type->AddOperation(new BasicOperation<Byte (Me::*)(), Byte (Me::*)() const>(type, "GetByteValue", &Me::GetByteValue));
    	type->AddOperation(new BasicOperation<UByte (Me::*)(), UByte (Me::*)() const>(type, "GetUByteValue", &Me::GetUByteValue));
    	type->AddOperation(new BasicOperation<UShort (Me::*)(), Short (Me::*)() const>(type, "GetShortValue", &Me::GetShortValue));
    	type->AddOperation(new BasicOperation<UShort (Me::*)(), UShort (Me::*)() const>(type, "GetUShortValue", &Me::GetUShortValue));
    	type->AddOperation(new BasicOperation<Int (Me::*)(), Int (Me::*)() const>(type, "GetIntValue", &Me::GetIntValue));
    	type->AddOperation(new BasicOperation<UInt (Me::*)(), UInt (Me::*)() const>(type, "GetUIntValue", &Me::GetUIntValue));
    	type->AddOperation(new BasicOperation<BigInt (Me::*)(), BigInt (Me::*)() const>(type, "GetBigIntValue", &Me::GetBigIntValue));

    	type->AddOperation(new BasicOperation<float (Me::*)(), float (Me::*)() const>(type, "GetFloatValue", &Me::GetFloatValue));
    	type->AddOperation(new BasicOperation<double (Me::*)(), double (Me::*)() const>(type, "GetDoubleValue", &Me::GetDoubleValue));

    	type->AddOperation(new BasicOperation<Int (Me::*)(Number*), Int (Me::*)(const Number*) const>(type, "Compare", "number", &Me::Compare));

    	type->AddOperation(new BasicOperation<void (Me::*)(Byte)>(type,   "SetByteValue", "value", &Me::SetByteValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(UByte)>(type,  "SetUByteValue", "value", &Me::SetUByteValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(Short)>(type,  "SetShortValue", "value", &Me::SetShortValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(UShort)>(type, "SetUShortValue", "value", &Me::SetUShortValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int)>(type,    "SetIntValue", "value", &Me::SetIntValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(UInt)>(type,   "SetUIntValue", "value", &Me::SetUIntValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(BigInt)>(type, "SetBigIntValue", "value", &Me::SetBigIntValue));

    	type->AddOperation(new BasicOperation<void (Me::*)(float)>(type,  "SetFloatValue", "value", &Me::SetFloatValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(double)>(type, "SetDoubleValue", "value", &Me::SetDoubleValue));


    	type->AddOperation(new BasicOperation<void (Me::*)(Byte)>(type,   "SetValue", "value", &Me::SetValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(UByte)>(type,  "SetValue", "value", &Me::SetValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(Short)>(type,  "SetValue", "value", &Me::SetValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(UShort)>(type, "SetValue", "value", &Me::SetValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int)>(type,    "SetValue", "value", &Me::SetValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(UInt)>(type,   "SetValue", "value", &Me::SetValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(BigInt)>(type, "SetValue", "value", &Me::SetValue));

    	type->AddOperation(new BasicOperation<void (Me::*)(float)>(type,  "SetValue", "value", &Me::SetValue));
    	type->AddOperation(new BasicOperation<void (Me::*)(double)>(type, "SetValue", "value", &Me::SetValue));
    }


	CUBEFS_DECLARE_REFLECTION;
};

template <typename Interface>
Type* NumberImplT<Interface>::type_ 					= NULL;

typedef NumberImplT<Number>								NumberImpl;

template <typename T, typename Interface>
class TypedNumberImplT: public NumberImplT<Interface> {
	typedef TypedNumberImplT<T, Interface> Me;
public:
    TypedNumberImplT(const T &value): value_(value) {}
    TypedNumberImplT(): value_(0) {}

    virtual Int     GetIntValue() const {
    	return value_;
    }

    virtual UInt    GetUIntValue() const {
    	return value_;
    }

    virtual Byte    GetByteValue() const {
    	return value_;
    }

    virtual UByte   GetUByteValue() const {
    	return value_;
    }

    virtual Short   GetShortValue() const {
    	return value_;
    }

    virtual UShort  GetUShortValue() const {
    	return value_;
    }

    virtual BigInt  GetBigIntValue() const {
    	return value_;
    }

    virtual float   GetFloatValue() const  {
    	return value_;
    }

    virtual double  GetDoubleValue() const {
    	return value_;
    }

    virtual void SetIntValue(Int value) {
    	value_ = value;
    }

    virtual void SetUIntValue(UInt value) {
    	value_ = value;
    }

    virtual void SetByteValue(Byte value) {
    	value_ = value;
    }

    virtual void SetUByteValue(UByte value) {
    	value_ = value;
    }

    virtual void SetShortValue(Short value) {
    	value_ = value;
    }

    virtual void SetUShortValue(UShort value) {
    	value_ = value;
    }

    virtual void SetBigIntValue(BigInt value) {
    	value_ = value;
    }

    virtual void SetFloatValue(float value) {
    	value_ = value;
    }

    virtual void SetDoubleValue(double value) {
    	value_ = value;
    }

    virtual void SetValue(Int value) {
    	value_ = value;
    }

    virtual void SetValue(UInt value) {
    	value_ = value;
    }

    virtual void SetValue(Byte value) {
    	value_ = value;
    }

    virtual void SetValue(UByte value) {
    	value_ = value;
    }

    virtual void SetValue(Short value) {
    	value_ = value;
    }

    virtual void SetValue(UShort value) {
    	value_ = value;
    }

    virtual void SetValue(BigInt value) {
    	value_ = value;
    }

    virtual void SetValue(float value) {
    	value_ = value;
    }

    virtual void SetValue(double value) {
    	value_ = value;
    }

    virtual void Set(const Value* value) {
    	if (value != NULL)
    	{
    		if (value->type() == type())
    		{
    			const Me* value0 = static_cast<const Me*>(value);
    			value_ = value0->GetValue();
    		}
    		else {
    			throw TypeCastException(CUBEFS_SOURCE, "Invalid argument type. It must be the same.", value->type(), type());
    		}
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Argument must not be null");
    	}
    }

    virtual const T& GetValue() const {
    	return value_;
    }

    virtual T& GetValue() {
    	return value_;
    }

    virtual void set(const void* value)  {
    	value_ = *(const T*)value;
    }

    virtual void get(void* value) const  {
    	*((T*)value) = value_;
    }

    T ToMyType(const Number* num) const
    {
    	if (num->IsInstance(TypedNumber<Byte>::MyType())) {
    		return num->GetByteValue();
    	}
    	else if (num->IsInstance(TypedNumber<UByte>::MyType())) {
    		return num->GetUByteValue();
    	}
    	else if (num->IsInstance(TypedNumber<Short>::MyType())) {
    		return num->GetShortValue();
    	}
    	else if (num->IsInstance(TypedNumber<UShort>::MyType())) {
    		return num->GetUShortValue();
    	}
    	else if (num->IsInstance(TypedNumber<Int>::MyType())) {
    		return num->GetIntValue();
    	}
    	else if (num->IsInstance(TypedNumber<UInt>::MyType())) {
    		return num->GetUIntValue();
    	}
    	else if (num->IsInstance(TypedNumber<BigInt>::MyType()))
    	{
    		return num->GetBigIntValue();
    	}
    	else if (num->IsInstance(TypedNumber<float>::MyType()))
    	{
    		return num->GetFloatValue();
    	}
    	else if (num->IsInstance(TypedNumber<double>::MyType()))
    	{
    		return num->GetDoubleValue();
    	}
    }

    virtual Int Compare(const Number* number) const
    {
    	if (number != NULL)
    	{
    		const T val0 = ToMyType(number);
    		if (value_ == val0) {
    			return 0;
    		}
    		else if (value_ > val0) {
    			return 1;
    		}
    		else {
    			return -1;
    		}
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Argument must not be null");
    	}
    }

    virtual bool IsEquals(const Value* val) const {
    	if (val != NULL && val->IsInstance(Interface::MyType()))
    	{
    		const Interface* v0 = static_cast<const Interface*>(val);
    		return value_ == v0->GetValue();
    	}
    	else {
    		return false;
    	}
    }

    static void InitType()
    {
        type_ = new AbstractType(TypeNameFactory<Interface>::name(), Number::MyType());
    }

    static void InitOperations()
    {
    	AbstractType* type = static_cast<AbstractType*>(Interface::MyType());
    	type->AddOperation(new BasicOperation<T (Me::*)(), const T& (Me::*)() const>(type, "GetValue", &Me::GetValue));
    }

private:
    T value_;

    CUBEFS_DECLARE_REFLECTION;
};

template <typename T, typename Interface>
Type* TypedNumberImplT<T, Interface>::type_ 						= NULL;

template <typename T>
class TypedNumberImpl: public TypedNumberImplT<T, TypedNumber<T> > 	{
	typedef TypedNumberImplT<T, TypedNumber<T> > Base;

public:
	TypedNumberImpl(const T& value): Base(value){}
	TypedNumberImpl(): Base() {}
};


#define CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(TypeName)	\
template <>												\
struct TypeFactory<TypeName> {							\
    static Type* type() {								\
        return TypedNumberImpl<TypeName>::MyType();		\
    }													\
}

CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(Byte);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(UByte);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(Short);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(UShort);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(Int);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(UInt);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(Long);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(BigInt);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(float);
CUBEFS_DECLARE_NUMBER_TYPE_FACTORY(double);


#define CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(TypeName)									\
template <>																						\
struct TypedWrapperFactory<TypeName>: TypedWrapperFactoryBase<TypedNumberImpl<TypeName>, TypeName> {}

CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(Int);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(UInt);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(Long);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(Short);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(UShort);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(Byte);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(UByte);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(BigInt);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(float);
CUBEFS_DECLARE_TYPED_NUMBER_WARAPPER_FACTORY(double);


#define CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(TypeName)			\
template <>                                         			\
struct ParameterTypeHelper<TypeName> {             				\
    static TypeName convert(Typed* value) {        				\
        return static_cast<TypedNumber<TypeName>*>(value)->GetValue();\
    }                                               			\
}

CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(Int);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(UInt);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(Long);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(Short);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(UShort);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(Byte);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(UByte);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(BigInt);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(float);
CUBEFS_DEFINE_NUMBER_PARAMETER_TYPE(double);

}}

#undef CUBEFS_SOURCE
#endif
